#ifndef __HRADWARE_BUFFER_H__
#define __HRADWARE_BUFFER_H__

#include "SystemMemBuffer.h"
#include "RenderingConfig.h"

namespace Nezha
{

	class NEZHA_ENGINE_ENTRY HardwareBuffer : public Refable
	{
	public:
		enum Usage
		{
			HBU_STATIC,
			HBU_DYNAMIC,
			HBU_TEXTURE,
			HBU_COLORBUFFER,
			HBU_DEPTH,
			HBU_STENCIL,
			HBU_DEPTHSTENCIL,

			HBU_NUM
		};

		enum LockOption
		{
			HBL_READ_ONLY,
			HBL_WRITE_ONLY,
			HBL_READ_WRITE,
			HBL_DISCARD,			// both read & write, but discard whole buffer and retrive a new buffer.
									// only allowed on buffer created with HBU_DYNAMIC flag.

			HBL_NUM
		};

	public:
		HardwareBuffer(Usage usage, bool createShadowBuffer)
			:mUsage(usage), mUsingShadowBuffer(createShadowBuffer), mShadowBufferDirty(false)
			,mIsLocked(false), mSizeInBytes(0)
		{
			//SystemMemBufferPtr smb(&mLocalMemBuffer);
			//mShadowBuffer.reset(0, mSizeInBytes, smb);
		}

		virtual ~HardwareBuffer() {}

		virtual void* lock(u32 offset, u32 length, LockOption opt)
		{
			NZ_ASSERT_EX(!isLocked(), "Buffer is already locked!");

			NZ_ASSERT_EX((length + offset) <= mSizeInBytes, "Lock request out of bounds");

			if(mUsingShadowBuffer)
			{
				mIsLocked = true;
				return lockShadowBuffer(offset, length);
			}

			void* ret = lockImpl(offset, length, opt);
			mIsLocked = true;

			return ret;
		}

		void* lock(LockOption opt)
		{
			return lock(0, mSizeInBytes, opt);
		}

		virtual void unlock()
		{
			if(mIsLocked)
			{
				if(mUsingShadowBuffer)
				{
					mShadowBufferDirty = true;
				}
				else
				{
					unlockImpl();
				}

				mIsLocked = false;
			}
		}

		// tell the device to flush the submit, most of time, this is dispatched by renderer.
		void submit()
		{
			if(mShadowBufferDirty)
			{
				syncShadowBuffer();
				mShadowBufferDirty = false;
			}
		}

		bool isLocked() const
		{
			return mIsLocked;
		}

		u32 getSize() const
		{
			return mSizeInBytes;
		}

		Usage getUsage() const
		{
			return mUsage;
		}

		bool usingShadowBuffer() const
		{
			return mUsingShadowBuffer;
		}
	protected:
		virtual void* lockImpl(u32 offset, u32 length, LockOption opt) = 0;
		virtual void unlockImpl() = 0;
		virtual void syncShadowBuffer(){}

		u8* lockShadowBuffer(u32 offset, u32 len)
		{
			NZ_ASSERT(offset + len <= mShadowBuffer.sizeInBytes());

			mShadowBufferDirty = false;
			return mShadowBuffer.data() ? (((u8*)mShadowBuffer.data()) + offset) : NULL;
		}

		u32 mSizeInBytes;
		Usage mUsage;
		bool mIsLocked;

		// for shadow buffer.
		SystemMemBuffer mShadowBuffer;
		//SystemMemBufferShared mShadowBuffer;
		bool mUsingShadowBuffer;
		bool mShadowBufferDirty;
	};

	typedef RefCountedPtr<HardwareBuffer>::Default HardwareBufferPtr;

}//end namespace Nezha

#endif //end __HRADWARE_BUFFER_H__